import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;


public class Sign 
{
	/*
	 * This class represents a node in the CCG derivation.
	 */
	boolean headOnLeft;
	static CategoryAnalyst analyst = new CategoryAnalyst();
	int headIndex; // the index of the "head" word of this constituent.
	String ptbPOS;
	SuperDuperTag cat; // CCG category
	Sign lParent; // left parent node, possibly null if this is a leaf
	Sign rParent; // right parent node, also possibly null
	ArrayList<Sign> heads; //The leaf node that is this Sign's head.  Could be self-referencial
	ArrayList<String> words; //The words that this sign covers.  
	HashSet<Dependency> dependencies; //The Hockenmaier-style PARG file
	ArrayList<DennisDependency> dennisDependencies;
	int numOfUnaryRules;
	ArrayList<Sign> thingsIHaveEaten;
	String createdBy;
	int hash;
	int complexity;
	int leftExtent;
	int rightExtent;
	
	
	boolean hasBeenTypeRaised;
	boolean hasBeenUnaryChanged;
	
	//For leaf nodes
	public Sign(String input, int headIndex)
	{
		headOnLeft = false;
		int divIndex = input.indexOf(":");
		this.words = new ArrayList<String>();
		this.words.add(input.substring(0,divIndex));
		this.cat = new SuperDuperTag(input.substring(divIndex+1, input.length()));
		this.headIndex = headIndex;
		heads = new ArrayList<Sign>();
		heads.add(this);
		dependencies = new HashSet<Dependency>();
		dennisDependencies = new ArrayList<DennisDependency>();
		thingsIHaveEaten = new ArrayList<Sign>();
		hasBeenTypeRaised = false;
		hash = 7;
		complexity = 0;
	}
	
	//For non-leaf nodes
	public Sign(SuperDuperTag cat, Sign lParent, Sign rParent)
	{
		headOnLeft = false;
		leftExtent = lParent.getLeftExtent();
		rightExtent = rParent.getRightExtent();
		this.cat = new SuperDuperTag(cat);
		this.lParent = lParent;
		this.rParent = rParent;
		this.words = new ArrayList<String>();
		this.words.addAll(lParent.getWordList());
		this.words.addAll(rParent.getWordList());
		heads = new ArrayList<Sign>();
		this.dependencies = new HashSet<Dependency>(lParent.getPargFile());
		this.dependencies.addAll(rParent.getPargFile());
		this.dennisDependencies = new ArrayList<DennisDependency>(lParent.getDennisDependencies());
		this.dennisDependencies.addAll(rParent.getDennisDependencies());
		thingsIHaveEaten = new ArrayList<Sign>();
		hasBeenTypeRaised = false;
		hash = 31 * cat.hashCode() + lParent.hashCode() + rParent.hashCode();
		complexity = lParent.getComplexity() + rParent.getComplexity() + 1;
	}
	
	public Sign(Sign unaryParent)
	{
		headOnLeft = false;
		leftExtent = unaryParent.getLeftExtent();
		rightExtent = unaryParent.getRightExtent();
		this.cat = new SuperDuperTag(unaryParent.getSuperDuperTag());
		this.lParent = unaryParent;
		this.rParent = null;
		this.words = unaryParent.getWordList();
		heads = new ArrayList<Sign>();
		this.dependencies = new HashSet<Dependency>(unaryParent.getPargFile());
		this.dennisDependencies = unaryParent.getDennisDependencies();
		thingsIHaveEaten = unaryParent.getThingsIHaveEaten();
		hasBeenTypeRaised = true;
		hash = unaryParent.hashCode();
	}
	
	public boolean isHeadOnLeft()
	{
		return headOnLeft;
	}
	
	public void setHeadOnLeft()
	{
		headOnLeft = true;
	}
	
	public Sign getLeftParent()
	{
		return lParent;
	}
	
	public Sign getRightParent()
	{
		return rParent;
	}
	
	public void setExtent(int l, int r)
	{
		leftExtent = l;
		rightExtent = r;
	}
	
	public int getLeftExtent()
	{
		return leftExtent;
	}
	
	public int getRightExtent()
	{
		return rightExtent;
	}
	
	public void addToArgs(ArrayList<SuperDuperTag> args, ArrayList<String> slashes)
	{
		cat.addToArgs(args, slashes);
	}
	
	
	public void setThingsIHaveEaten(ArrayList<Sign> s)
	{
		thingsIHaveEaten = new ArrayList<Sign>(s);
	}
	
	public void incComplexity()
	{
		complexity++;
	}
	
	public int getComplexity()
	{
		return complexity;
	}
	
	public String getCreator()
	{
		return createdBy;
	}
	
	public void setCreator(String s)
	{
		createdBy = s;
	}
	
	public int getStringBaseIndex()
	{
		return cat.getIndexOfStringBase();
	}
	
	public int getStringIndex()
	{
		return headIndex;
	}
	
	public Sign getHead()
	{
		if(heads.size() > 0)
			return heads.get(0);
		else return null;
	}
	
	public ArrayList<Sign> getHeads()
	{
		return heads;
	}
	
	public void setSuperDuperTag(SuperDuperTag s)
	{
		cat = s;
	}
	
	public void addHead(Sign h)
	{
		heads.add(h);
	}
	
	public void addHeads(ArrayList<Sign> heads)
	{
		this.heads.addAll(heads);
	}

	public int getSemIndex()
	{
		return cat.getSemIndex();
	}
	
	public int getLeftmostIndex()
	{
		return cat.getLeftmostIndex();
	}
	
	public void eatElement(Sign input)
	{
		//System.out.println("eating: "+ input);
		thingsIHaveEaten.add(input);
		cat.consumeOne();
	}
	
	public Set<Sign> digForCoindexedElements(Sign x, int num)
	{
		int counter = 0;
		Set<Sign> result = new HashSet<Sign>();
		ArrayList<Sign> signList = getThingsIHaveEaten();
		
		for(SuperDuperTag spotlight : x.getBag())
		{
			if(spotlight.getIndexOfStringBase() == x.getStringBaseIndex())
			{
				//System.out.println("Found an external head:" + x.getSemIndex() + " " + spotlight.getSemIndex());
				result.add(signList.get(counter).getHead());
			}
			counter++;
		}
		return result;
		
	}
	
	public boolean hasBeenTypeRaised()
	{
		return hasBeenTypeRaised;
	}
	
	public ArrayList<SuperDuperTag> getBag()
	{
		return cat.getBag();
	}
	
	public ArrayList<Sign> getThingsIHaveEaten()
	{
		return thingsIHaveEaten;
	}
	
	public int getNumOfArgs()
	{
		return cat.getNumOfArgs();
	}
	
	public HashSet<Dependency> getPargFile()
	{
		return dependencies;
	}
	
	/*
	 * Returns the Hockenmaier-style PARG relations for
	 * everything that has been parsed under this sign.
	 */
	
	public String pargFileToParg()
	{
		String result = "";
		Iterator i = dependencies.iterator();
		while(i.hasNext())
		{
			Dependency pl = (Dependency) i.next();
			result += pl.toPARG() + "\n";
		}
		return result;
	}
	
	public String printDennisDependencies()
	{
		String result = "";
		Iterator i = dennisDependencies.iterator();
		while(i.hasNext())
		{
			DennisDependency d = (DennisDependency) i.next();
			result += d.toString() + "\n";
		}
		return result;
	}
	
	public void addToDennisDependencies(DennisDependency d)
	{
		dennisDependencies.add(d);
	}
	
	public ArrayList<DennisDependency> getDennisDependencies()
	{
		return dennisDependencies;
	}
	
	public void addToParglist(Dependency input)
	{
		dependencies.add(input);
		hash = 31 * hash + input.hashCode();
	}
	
	public ArrayList<String> getWordList()
	{
		return words;
	}
	
	public String getWords()
	{
		String result = "";
		Iterator<String> i = words.iterator();
		while(i.hasNext())
		{
			result+= i.next() + " ";
		}
		return result;
	}
	
	public SuperDuperTag getSuperDuperTag()
	{
		return cat;
	}

	
	public int hashCode()
	{
		return dennisDependencies.hashCode();
	}
	
	/*OLD EQUALS
	public boolean equals(Object o)
	{
		if(this == o)
			return true;
		else if (this.getClass() != o.getClass())
			return false;
		else
		{
			Sign s = (Sign) o;
			HashSet dep = s.getPargFile();
			return(cat.equals(s.getSuperDuperTag()) && dependencies.equals(dep) && hasBeenTypeRaised == s.hasBeenTypeRaised());
		}
	}*/
	
	public boolean equals(Object o)
	{
		if(this == o)
			return true;
		else if (this.getClass() != o.getClass())
			return false;
		else
		{
			Sign s = (Sign) o;
			ArrayList<DennisDependency> dep = s.getDennisDependencies();
			return(cat.equals(s.getSuperDuperTag()) && dennisDependencies.containsAll(dep));
		}
	}
	
	public String toString()
	{
		return cat.toString();
	}
	
	public String toStringReadable()
	{
		return cat.toStringReadable();
	}
	
	public int getNormalFormVal()
	{
		int result = 0;
		for(Dependency spotlight : dependencies)
		{
			if(!spotlight.isNormalForm())
			{
				result++;
			}
		}
		return result;
	}
	
	
	
}
